

/**
 * @file DataStructures.hpp
 * Set of several data structures to be used by other GPSTk classes.
 */

#ifndef GPSTK_DATASTRUCTURES_HPP
#define GPSTK_DATASTRUCTURES_HPP

//============================================================================
//
//
//  Dagoberto Salazar - gAGE ( http://www.gage.es ). 2007, 2008, 2009, 2011
//
//  add globalGnssData, shjzhang, 2019/05/06
//  remove globalGnssData, shjzhang, 2019/07/7
//
//  remove sourceHeader and sourceEpochHeader
//  remove some methods of the data headers
//  remove Class of 'DataHeaders'
//  shjzhang, 2019/07/07
//
//  2020/01/20
//  add getValue for struct sourceTypeValueMap
//  shjzhang.
//
//============================================================================


#include <utility>
#include <vector>
#include <set>
#include <map>
#include <string>

#include "FFData.hpp"
#include "Rinex3ObsStream.hpp"
#include "Rinex3ObsData.hpp"
#include "StringUtils.hpp"
#include "Vector.hpp"
#include "Matrix.hpp"
#include "CivilTime.hpp"
#include "YDSTime.hpp"
#include "constants.hpp"

#include "TypeID.hpp"
#include "SatID.hpp"
#include "SourceID.hpp"
#include "Antenna.hpp"

using namespace std;

namespace gpstk
{

      /** @defgroup DataStructures GPSTk data structures
       *
       * This is a set of several data structures to be used by other
       * GPSTk classes.
       *
       * Each data structure is composed of a header and a body. The header
       * contains the information that is common to all the data stored in
       * the structure, and the body contains the data themselves along with
       * the information (indexes) necessary to access them.
       *
       * In this regard, four basic indexes are considered enough to
       * completely identify any GNSS value:
       *
       *  \li Receiver/Source (SourceID)
       *  \li Epoch (CommonTime)
       *  \li Satellite (SatID)
       *  \li Type of value (TypeID)
       *
       * Moreover, all the GNSS data structures have two main parts:
       *
       *  \li Header: Containing the indexes that are common to all the values
       *              (sometimes with some extra information).
       *
       *  \li Body: Containing the GNSS values themselves, organized in
       *            std::maps.
       *
       * The general idea is to use the GNSS data structures like WHITE BOXES
       * that are able to carry all the important data around in an easy way,
       * in order to do something like the following to process GNSS data:
       *
       * @code
       *   RinexObsStream rin("bahr1620.04o"); // Create the input file stream
       *   gnssRinex gRin;                     // Declare a gnssRinex object
       *
       *   ModeledPR modelPR;          // Declare a ModeledReferencePR object
       *   SolverLMS solver;           // Declare an object to apply LMS method
       *
       *
       *   // ... other inicialization code here ...
       *
       *
       *   // Feed the gRin data structure
       *   while(rin >> gRin)
       *   {
       *
       *      gRin.keepOnlyTypeID(TypeID::C1) >> modelPR >> solver;
       *
       *      // Print the results for this epoch
       *      cout << gRin.header.epoch.DOYsecond() << "  ";   // Epoch
       *      cout << solver.solution[0] << "  ";              // dx
       *      cout << solver.solution[1] << "  ";              // dy
       *      cout << solver.solution[2] << "  ";              // dz
       *
       *   }
       * @endcode
       *
       */

      //@{


      // First, we must declare some important exception objects


      /// Thrown when attempting to access a value and the corresponding TypeID
      /// does not exist in the map.
      /// @ingroup exceptiongroup
    NEW_EXCEPTION_CLASS(TypeIDNotFound, gpstk::Exception);


      /// Thrown when attempting to access a value and the corresponding SatID
      /// does not exist in the map.
      /// @ingroup exceptiongroup
    NEW_EXCEPTION_CLASS(SatIDNotFound, gpstk::Exception);
    NEW_EXCEPTION_CLASS(SystemNotFound, gpstk::Exception);


      /// Thrown when attempting to access a value and the corresponding
      /// epoch (CommonTime) does not exist in the map.
      /// @ingroup exceptiongroup
    NEW_EXCEPTION_CLASS(CommonTimeNotFound, gpstk::Exception);


      /// Thrown when attempting to access a value and any of the corresponding
      /// indexes (SourceID, SatID or TypeID) does not exist in the map.
      /// @ingroup exceptiongroup
    NEW_EXCEPTION_CLASS(ValueNotFound, gpstk::Exception);


      /// Thrown when the number of data values and the number of
      /// corresponding types does not match.
      /// @ingroup exceptiongroup
    NEW_EXCEPTION_CLASS(NumberOfTypesMismatch, gpstk::Exception);


      /// Thrown when the number of data values and the number of
      /// corresponding satellites does not match.
      /// @ingroup exceptiongroup
    NEW_EXCEPTION_CLASS(NumberOfSatsMismatch, gpstk::Exception);


       /// Thrown when satellite number is less than 4
       /// @ingroup exceptiongroup
    NEW_EXCEPTION_CLASS(SVNumException, gpstk::Exception);

       /// throw exception
    NEW_EXCEPTION_CLASS(SourceIDNotFound, gpstk::Exception);

       /// some useful type definition

       /// Set containing TypeID objects.
    typedef std::set<TypeID> TypeIDSet;

       /// Set containing SatID objects.
    typedef std::set<SatID> SatIDSet;

       /// Set containing SourceID objects.
    typedef std::set<SourceID> SourceIDSet;

       /// Map holding sat with corresponding Vector<double>.
    typedef std::map< SatID, Vector<double> > satVectorMap;

       /// Map holding TypeID with corresponding numeric value.
    struct typeValueMap : std::map<TypeID, double>
    {

         /// Return the number of different types available.
        inline size_t numTypes() const
        { return (*this).size(); }


         /// Return a TypeIDSet with all the data types present in
         /// this object.
        TypeIDSet getTypeID() const;


         /// Return a typeValueMap with only this type of data.
         /// @param type Type of value to be extracted.
        typeValueMap extractTypeID(const TypeID& type) const;


         /// Return a typeValueMap with only these types of data.
         /// @param typeSet Set (TypeIDSet) containing the types of data to
         ///                be extracted.
        typeValueMap extractTypeID(const TypeIDSet& typeSet) const;


         /// Modifies this object, keeping only this type of data.
         /// @param type Type of value to be kept.
        typeValueMap& keepOnlyTypeID(const TypeID& type);


         /// Modifies this object, keeping only these types of data.
         /// @param typeSet Set (TypeIDSet) containing the types of data
         ///                to be kept.
        typeValueMap& keepOnlyTypeID(const TypeIDSet& typeSet);


         /// Modifies this object, removing this type of data.
         /// @param type Type of value to be removed.
        typeValueMap& removeTypeID(const TypeID& type)
        { (*this).erase(type); return (*this); }


         /// Modifies this object, removing these types of data.
         /// @param typeSet Set (TypeIDSet) containing the types of data
         ///                to be kept.
        typeValueMap& removeTypeID(const TypeIDSet& typeSet);


         /** Return the data value (double) corresponding to provided type.
          *
          * @param type       Type of value to be looked for.
          */
        double getValue(const TypeID& type) const
            noexcept(false);


         /// Return a reference to the data value (double) with
         /// corresponding type.
         /// @param type Type of value to be looked for.
        double& operator()(const TypeID& type)
            noexcept(false);


         /// Convenience output method
        virtual std::ostream& dump( std::ostream& s,
                                    int mode = 0 ) const;


         /// Destructor.
        virtual ~typeValueMap() {};

    };  // End typeValueMap



      /// Map holding SatID with corresponding numeric value.
    struct satValueMap : std::map<SatID, double>
    {

         /// Return the number of satellites available.
        size_t numSats() const
        { return (*this).size(); }


         /// Return a SatIDSet with all the satellites present in this object.
        SatIDSet getSatID() const;


         /// Return a Vector with all the satellites present in this object.
        Vector<SatID> getVectorOfSatID() const;


         /// Return a satValueMap with only this satellite.
         /// @param satellite Satellite to be extracted.
        satValueMap extractSatID(const SatID& satellite) const;


         /// Return a satValueMap with only one satellite, identified by
         /// the given parameters.
         /// @param p Satellite PRN number.
         /// @param p System the satellite belongs to.
        satValueMap extractSatID( const int& p,
                                  const SatelliteSystem::Systems& s ) const;


         /// Return a satValueMap with only these satellites.
         /// @param satSet Set (SatIDSet) containing the satellites to
         ///               be extracted.
        satValueMap extractSatID(const SatIDSet& satSet) const;


         /// Modifies this object, keeping only this satellite.
         /// @param satellite Satellite to be kept.
        satValueMap& keepOnlySatID(const SatID& satellite);


         /// Modifies this object, keeping only this satellite.
         /// @param p Satellite PRN number.
         /// @param p System the satellite belongs to.
        satValueMap& keepOnlySatID( const int& p,
                                    const SatelliteSystem::Systems& s );


         /// Modifies this object, keeping only these satellites.
         /// @param satSet Set (SatIDSet) containing the satellites to be kept.
        satValueMap& keepOnlySatID(const SatIDSet& satSet);


         /// Modifies this object, removing this satellite.
         /// @param satellite Satellite to be removed.
        satValueMap& removeSatID(const SatID& satellite)
        { (*this).erase(satellite); return (*this); }


         /// Modifies this object, removing the given satellites.
         /// @param satSet Set (SatIDSet) containing the satellites to
         ///               be removed.
        satValueMap& removeSatID(const SatIDSet& satSet);


         /** Return the data value (double) corresponding to provided SatID.
          *
          * @param satellite     Satellite to be looked for.
          */
        double getValue(const SatID& satellite) const
            noexcept(false);


         /// Return a reference to the data value (double) with
         /// corresponding SatID.
         /// @param satellite Satellite to be looked for.
        double& operator()(const SatID& satellite)
            noexcept(false);

         /// Convenience output method
        virtual std::ostream& dump( std::ostream& s,
                                    int mode = 0 ) const;

         /// Destructor.
        virtual ~satValueMap() {};

    };  // End of 'satValueMap'



      /// Map holding SourceID with corresponding numeric value.
    struct sourceValueMap : std::map<SourceID, double>
    {

         /// Return the number of satellites available.
        size_t numSources() const
        { return (*this).size(); }


         /// Return a SourceIDSet with all the satellites present in this object.
        SourceIDSet getSourceID() const;


         /// Return a Vector with all the sources present in this object.
        Vector<SourceID> getVectorOfSourceID() const;


         /// Return a sourceValueMap with only this source.
         /// @param source Source to be extracted.
        sourceValueMap extractSourceID(const SourceID& source) const;


         /// Return a sourceValueMap with only these sources.
         /// @param sourceSet Set (SourceIDSet) containing the sources to
         ///               be extracted.
        sourceValueMap extractSourceID(const SourceIDSet& sourceSet) const;


         /// Modifies this object, keeping only this source.
         /// @param source Source to be kept.
        sourceValueMap& keepOnlySourceID(const SourceID& source);


         /// Modifies this object, keeping only these sources.
         /// @param sourceSet Set (SourceIDSet) containing the sources to be kept.
        sourceValueMap& keepOnlySourceID(const SourceIDSet& sourceSet);


         /// Modifies this object, removing this source.
         /// @param source Source to be removed.
        sourceValueMap& removeSourceID(const SourceID& source)
        { (*this).erase(source); return (*this); }


         /// Modifies this object, removing the given sources.
         /// @param sourceSet Set (SourceIDSet) containing the sources to
         ///               be removed.
        sourceValueMap& removeSourceID(const SourceIDSet& sourceSet);


         /** Return the data value (double) corresponding to provided SourceID.
          *
          * @param source     Source to be looked for.
          */
        double getValue(const SourceID& source) const
            noexcept(false);


         /// Return a reference to the data value (double) with
         /// corresponding SourceID.
         /// @param source Source to be looked for.
        double& operator()(const SourceID& source)
            noexcept(false);


         /// Destructor.
        virtual ~sourceValueMap() {};

    };  // End of 'sourceValueMap'


      /// Map holding SatID with corresponding typeValueMap.
    struct satTypeValueMap : std::map<SatID, typeValueMap>
    {

         /// Return the number of available satellites.
        size_t numSats() const
        { return (*this).size(); }


         /// Return the number of available satellites.
        size_t numSats(SatelliteSystem::Systems sys) const
        { 
            int nsat(0);
            for(auto it=(*this).begin(); it!=(*this).end(); it++)
            {
                if((*it).first.system == sys)
                {
                    nsat ++;
                }
            }
            return nsat;
        }

         /** Return the total number of data elements in the map.
          * This method DOES NOT suppose that all the satellites have
          * the same number of type values.
          */
        size_t numElements() const;


         /// Return a SatIDSet with all the satellites present in this object.
        SatIDSet getSatID() const;


         /// Return a Vector with all the satellites present in this object.
        Vector<SatID> getVectorOfSatID() const;


         /// Return a TypeIDSet with all the data types present in
         /// this object.  This does not imply that all satellites have
         /// these types.
        TypeIDSet getTypeID() const;


         /// Return a satTypeValueMap with only this satellite.
         /// @param satellite Satellite to be extracted.
        satTypeValueMap extractSatID(const SatID& satellite) const;


         /// Return a satTypeValueMap with only one satellite, identified
         /// by the given parameters.
         /// @param p Satellite PRN number.
         /// @param p System the satellite belongs to.
        satTypeValueMap extractSatID( const int& p,
                                      const SatelliteSystem::Systems& s) const;


         /// Return a satTypeValueMap with only these satellites.
         /// @param satSet Set (SatIDSet) containing the satellites to
         ///               be extracted.
        satTypeValueMap extractSatID(const SatIDSet& satSet) const;


         /// Modifies this object, keeping only this satellite.
         /// @param satellite Satellite to be kept.
        satTypeValueMap& keepOnlySatID(const SatID& satellite);


         /// Modifies this object, keeping only this satellite.
         /// @param p Satellite PRN number.
         /// @param p System the satellite belongs to.
        satTypeValueMap& keepOnlySatID( const int& p,
                                        const SatelliteSystem::Systems& s );


         /// Modifies this object, keeping only these satellites.
         /// @param satSet Set (SatIDSet) containing the satellites to be kept.
        satTypeValueMap& keepOnlySatID(const SatIDSet& satSet);


         /// Return a satTypeValueMap with only this type of value.
         /// @param type Type of value to be extracted.
        satTypeValueMap extractTypeID(const TypeID& type) const;


         /// Return a satTypeValueMap with only these types of data.
         /// @param typeSet Set (TypeIDSet) containing the types of data
         ///                to be extracted.
        satTypeValueMap extractTypeID(const TypeIDSet& typeSet) const;


         /// Modifies this object, keeping only this type of data.
         /// @param type Type of value to be kept.
        satTypeValueMap& keepOnlyTypeID(const TypeID& type);


         /// Modifies this object, keeping only these types of data.
         /// @param typeSet Set (TypeIDSet) containing the types of data
         ///                to be kept.
        satTypeValueMap& keepOnlyTypeID(const TypeIDSet& typeSet);


         /// Modifies this object, removing this satellite.
         /// @param satellite Satellite to be removed.
        satTypeValueMap& removeSatID(const SatID& satellite)
        { (*this).erase(satellite); return (*this); }


         /// Modifies this object, removing these satellites.
         /// @param satSet Set (SatIDSet) containing the satellites
         ///               to be removed.
        satTypeValueMap& removeSatID(const SatIDSet& satSet);


         /// Modifies this object, removing this type of data.
         /// @param type Type of value to be removed.
        satTypeValueMap& removeTypeID(const TypeID& type);


         /// Modifies this object, removing these types of data.
         /// @param typeSet Set(TypeIDSet) containing the types
         ///               to be removed.
        satTypeValueMap& removeTypeID(const TypeIDSet& typeSet);


         /// Return a GPSTk::Vector containing the data values with this type.
         /// @param type Type of value to be returned.
         /// This method returns zero if a given satellite does not have
         /// this type.
        Vector<double> getVectorOfTypeID(const TypeID& type) const;

         /// Return a GPSTk::Vector containing the data values with this type.
         /// @param type Type of value to be returned.
         /// This method returns zero if a given satellite does not have
         /// this type.
        Vector<double> getVectorOfTypeID(const TypeID& type, 
                                         SatelliteSystem::Systems sys) const;


         /// Return a GPSTk::Matrix containing the data values in this set.
         /// @param typeSet  TypeIDSet of values to be returned.
        Matrix<double> getMatrixOfTypes(const TypeIDSet& typeSet) const;

        
         /// Return a GPSTk::Matrix containing the data values in this set.
         /// @param typeSet  TypeIDSet of values to be returned.
        Matrix<double> getMatrixOfTypes(const TypeIDSet& typeSet,
                                        SatelliteSystem::Systems sys) const;


         /** Modifies this object, adding one vector of data with this type,
          *  one value per satellite.
          *
          * If type already exists, data is overwritten. If the number of
          * values does not match with the number of satellites, a
          * NumberOfSatsMismatch exception is thrown.
          *
          * Given that dataVector does not store information about the
          * satellites the values correspond to, the user is held responsible
          * for having the data values stored in dataVector in the proper
          * order regarding the SatIDs in this object.
          *
          * @param type          Type of data to be added.
          * @param dataVector    GPSTk Vector containing the data to be added.
          */
        satTypeValueMap& insertTypeIDVector( const TypeID& type,
                                             const Vector<double> dataVector )
            noexcept(false);


         /** Modifies this object, adding a matrix of data, one vector
          *  per satellite.
          *
          * If types already exists, data is overwritten. If the number of
          * rows in matrix does not match with the number of satellites, a
          * NumberOfSatsMismatch exception is thrown. If the number of columns
          * in matrix does not match with the number of types in typeSet, a
          * NumberOfTypesMismatch exception is thrown.
          *
          * Given that dataMatrix does not store information about the
          * satellites and types the values correspond to, the user is held
          * responsible for having those data values stored in dataMatrix in
          * the proper order regarding the SatIDs in this object and the
          * provided typeSet.
          *
          * @param typeSet       Set (TypeIDSet) containing the types of data
          *                      to be added.
          * @param dataMatrix    GPSTk Matrix containing the data to be added.
          */
        satTypeValueMap& insertMatrix( const TypeIDSet& typeSet,
                                       const Matrix<double> dataMatrix )
            noexcept(false);


         /** Return the data value (double) corresponding to provided SatID
          *  and TypeID.
          *
          * @param satellite     Satellite to be looked for.
          * @param type          Type to be looked for.
          */
        double getValue( const SatID& satellite,
                         const TypeID& type ) const
            noexcept(false);


         /// Return a reference to the typeValueMap with corresponding SatID.
         /// @param type Type of value to be look for.
        typeValueMap& operator()(const SatID& satellite) ;


         /// Convenience output method
        virtual std::ostream& dump( std::ostream& s,
                                    int mode = 0 ) const;


         /// Destructor.
        virtual ~satTypeValueMap() {};

    };  // End of 'satTypeValueMap'

      /// Map holding SatID with corresponding typeValueMap.
    struct sourceTypeValueMap : std::map<SourceID, typeValueMap>
    {
         /// Modifies this object, keeping only this type of data.
         /// @param type Type of value to be kept.
        sourceTypeValueMap& keepOnlyTypeID(const TypeID& type);

         /// Modifies this object, keeping only these types of data.
         /// @param typeSet Set (TypeIDSet) containing the types of data
         ///                to be kept.
        sourceTypeValueMap& keepOnlyTypeID(const TypeIDSet& typeSet);


         /// Return a satTypeValueMap with only these types of data.
         /// @param typeSet Set (TypeIDSet) containing the types of data
         ///                to be extracted.
        sourceTypeValueMap extractTypeID(const TypeIDSet& typeSet) const;

         /** Return the data value (double) corresponding to provided SourceID
          *  and TypeID.
          *
          * @param satellite     Source to be looked for.
          * @param type          Type to be looked for.
          */
        double getValue( const SourceID& source,
                         const TypeID& type ) const
            noexcept(false);

    };

    struct systemTypeValueMap : std::map<SatelliteSystem::Systems, typeValueMap>
    {
        double getValue(const SatelliteSystem::Systems& system,
                        const TypeID& type) const;
    };

    typedef std::map<TypeID, std::vector<double>> typeVectorMap;
    typedef std::map<SatelliteSystem::Systems, typeVectorMap> systemTypeVectorMap;

    /// Map holding epoch with corresponding Vector<double>.
    typedef std::map< CommonTime, Vector<double> > epochVectorMap;

    /// Map holding epoch with corresponding Matrix<double>.
    typedef std::map< CommonTime, Matrix<double> > epochMatrixMap;

    /// Map holding source with corresponding satTypeValueMap.
    typedef std::map<SourceID, satValueMap> sourceSatValueMap;

    /// Map holding sat with corresponding sourceValueMap.
    typedef std::map<SatID, sourceValueMap> satSourceValueMap;


    /// Map holding source with corresponding Vector<double>.
    typedef std::map< SourceID, Vector<double> > sourceVectorMap;

    /// Map holding source with corresponding Matrix<double>.
    typedef std::map< SourceID, Matrix<double> > sourceMatrixMap;

    /// Map holding sat with corresponding Matrix<double>.
    typedef std::map< SatID, Matrix<double> > satMatrixMap;

    /// Map holding source with corresponding satVectorMap.
    typedef std::map<SourceID, satVectorMap> sourceSatVectorMap;

    /// Map holding sat with corresponding sourceVectorMap.
    typedef std::map<SatID, sourceVectorMap> satSourceVectorMap;

    /// Map holding epoch with corresponding sourceVectorMap.
    typedef std::map<CommonTime, sourceVectorMap> epochSourceVectorMap;

    /// Map holding epoch with corresponding satVectorMap.
    typedef std::map<CommonTime, satVectorMap> epochSatVectorMap;

    /// Map holding epoch with corresponding sourceMatrixMap.
    typedef std::map<CommonTime, sourceMatrixMap> epochSourceMatrixMap;

    /// Map holding epoch with corresponding satMatrixMap.
    typedef std::map<CommonTime, satMatrixMap> epochSatMatrixMap;

    /// Map holding source with corresponding epochVectorMap.
    typedef std::map<SourceID, epochVectorMap> sourceEpochVectorMap;

    /// Map holding sat with corresponding epochVectorMap.
    typedef std::map<SatID, epochVectorMap> satEpochVectorMap;

    /// Map holding source with corresponding epochMatrixMap.
    typedef std::map<SourceID, epochMatrixMap> sourceEpochMatrixMap;

    /// Map holding sat with corresponding epochMatrixMap.
    typedef std::map<SatID, epochMatrixMap> satEpochMatrixMap;


    /// Map holding epoch with corresponding satTypeValueMap.
    typedef std::map<CommonTime, satTypeValueMap>  epochSatTypeValueMap;

    /// Map holding epoch with corresponding satValueMap.
    typedef std::map<CommonTime, satValueMap> epochSatValueMap;

    /// Map holding epoch with corresponding typeValueMap.
    typedef std::map<CommonTime, typeValueMap> epochTypeValueMap;

    /// Map holding source with corresponding satTypeValueMap.
    typedef std::map<CommonTime, sourceSatValueMap> epochSourceSatValueMap;


    struct YawData
    {
        YawData()
            : type("UNK"), beta(0.0),
              event(0), nominal(0.0), modeled(0.0)
        {};

        std::string type;
        double beta;
        int event;
        double nominal;
        double modeled;
    };

     // Now, some useful type definitions


    typedef std::map<SatID,YawData> satYawDataMap;

       /// Set of several headers to be used with data structures.

    struct OrbitData
    {
        Vector<double> pos;
        Vector<double> vel;
        Vector<double> SRPC;

        Matrix<double> dRdR;
        Matrix<double> dRdV;
        Matrix<double> dRdP;
        Matrix<double> dVdR;
        Matrix<double> dVdV;
        Matrix<double> dVdP;
    };

    typedef std::map<SatID, OrbitData> SatOrbitDataMap;


    typedef std::map<SourceID,int> SourceIntMap;
    typedef std::map<SatID,int> SatIntMap;
    typedef std::map< SourceID,std::map<SatID,int> > SourceSatIntMap;


    /// Map holding sat with corresponding sourceValueMap.
    typedef std::map<SatID, sourceTypeValueMap> satDataMap;


    struct EstData
    {
       EstData()
           : value(0.0), variance(1.0E+10)
       {}
       double value;
       double variance;
    };


       /// Defines a header containing the source and epoch of data, plus extra
       /// data extracted from a Rinex file
    struct gnssRinexHeader 
    {

          /// Field containing the epoch of data
       CommonTime epoch;

       SourceID source;

       short  epochFlag;

          /// Triple holding receiver related vars
       std::string  srcAntType;
       double  srcClock;
       Triple  srcPos;

       Triple  srcTide;
       Triple  srcMonument;
       Antenna srcAntenna;


          /// Default constructor
       gnssRinexHeader() {};

          /// Copy constructor
       gnssRinexHeader(const gnssRinexHeader& grh)
           : epoch(grh.epoch),
             epochFlag(grh.epochFlag),
             source(grh.source),
             srcAntType( grh.srcAntType),
             srcClock( grh.srcClock),
             srcPos( grh.srcPos),
             srcTide( grh.srcTide),
             srcMonument( grh.srcMonument),
             srcAntenna( grh.srcAntenna)
       {};


          /// Assignment operator
       virtual gnssRinexHeader& operator=( const gnssRinexHeader& right);


          /// Convenience output method for gnssRinexHeader
       virtual std::ostream& dump(std::ostream& s) const;


          /// Destructor
       virtual ~gnssRinexHeader() {};


    }; // End of struct 'gnssRinexHeader'



       /// stream output for gnssRinexHeader
    std::ostream& operator<<( std::ostream& s,
                              const gnssRinexHeader& grh );


       /// Defines a header containing the source and epoch of data, plus extra
       /// data extracted from a Rinex file
    struct sourceRinexHeader 
    {

       CommonTime epoch;

       SourceIDSet  sourceSet;

       std::map<SourceID, short>   srcEpochFlagData;
       std::map<SourceID, string>  srcAntTypeData;

       std::map<SourceID, double>  srcClockData;
       std::map<SourceID, Triple>  srcPosData;
       std::map<SourceID, Triple>  srcTideData;
       std::map<SourceID, Triple>  srcMonumentData;
       std::map<SourceID, Antenna> srcAntennaData;


          /// Default constructor
       sourceRinexHeader() {};


          /// Default constructor
       sourceRinexHeader(gnssRinexHeader& grh) 
       {
           epoch = grh.epoch;

           SourceID source(grh.source);
           sourceSet.insert(source);

           srcEpochFlagData[source] = grh.epochFlag;
           srcAntTypeData[source]   = grh.srcAntType;
           srcClockData[source]     = grh.srcClock;
           srcPosData[source]       = grh.srcPos;
           srcTideData[source]      = grh.srcTide;
           srcMonumentData[source]  = grh.srcMonument;
           srcAntennaData[source]   = grh.srcAntenna;

       };


       gnssRinexHeader getGnssRinexHeader( const SourceID& src) const
       {
           gnssRinexHeader toReturn;

           toReturn.epoch         =  epoch; 

           bool foundSource(false);

              // find source from sourceSet
           SourceIDSet::const_iterator itSrc1=sourceSet.find(src);
           if( itSrc1 != sourceSet.end() )
           {
               toReturn.source = (*itSrc1);
               foundSource = true;
           }

           if( !foundSource)
           {
               GPSTK_THROW(SourceIDNotFound("source not found"));
           }

           map<SourceID, short>::const_iterator itSrc2=srcEpochFlagData.find(src);
           if( itSrc2 != srcEpochFlagData.end() )
           {
               toReturn.epochFlag  = (*itSrc2).second;
           }

           map<SourceID, string>::const_iterator itSrc3=srcAntTypeData.find(src);
           if( itSrc3 != srcAntTypeData.end() )
           {
               toReturn.srcAntType= (*itSrc3).second;
           }

           map<SourceID, double>::const_iterator itSrc4=srcClockData.find(src);
           if( itSrc4 != srcClockData.end() )
           {
               toReturn.srcClock = (*itSrc4).second;
           }

           map<SourceID, Triple>::const_iterator itSrc5=srcPosData.find(src);
           if( itSrc5 != srcPosData.end() )
           {
               toReturn.srcPos = (*itSrc5).second;
           }

           map<SourceID, Triple>::const_iterator itSrc6=srcTideData.find(src);
           if( itSrc6 != srcTideData.end() )
           {
               toReturn.srcTide = (*itSrc6).second;
           }

           map<SourceID, Triple>::const_iterator itSrc7=srcMonumentData.find(src);
           if( itSrc7 != srcMonumentData.end() )
           {
               toReturn.srcMonument = (*itSrc7).second;
           }

           map<SourceID, Antenna>::const_iterator itSrc8=srcAntennaData.find(src);
           if( itSrc8 != srcAntennaData.end() )
           {
               toReturn.srcAntenna = (*itSrc8).second;
           }

           return toReturn;

       };

          /// Assignment operator
       virtual sourceRinexHeader& operator=(const sourceRinexHeader& right);

          /// Assignment operator from a epochSourceHeader
       virtual sourceRinexHeader& operator=(const gnssRinexHeader& right)
       { 

           // only assign observation-related data
           epoch = right.epoch;

           sourceSet.insert(right.source);

           srcEpochFlagData[right.source]   = right.epochFlag;
           srcAntTypeData[right.source]     = right.srcAntType;
           srcClockData[right.source]       = right.srcClock;
           srcPosData[right.source]         = right.srcPos;
           srcTideData[right.source]        = right.srcTide;
           srcMonumentData[right.source]    = right.srcMonument;
           srcAntennaData[right.source]     = right.srcAntenna;

           return (*this); 
       };


          /// Convenience output method for gnssRinexHeader
       virtual std::ostream& dump(std::ostream& s) const;

          /// Destructor
       virtual ~sourceRinexHeader() {};


    }; // End of struct 'sourceRinexHeader'

       /// stream output for gnssRinexHeader
    std::ostream& operator<<( std::ostream& s,
                              const sourceRinexHeader& grh );

       ///
       /// Basic gnssData structure.
       ///
    template <class HEADER_CLASS, class BODY_CLASS>
    struct gnssData
    {

        /// Header.
        HEADER_CLASS header;

        /// Body.
        BODY_CLASS   body;

        /// Default constructor.
        gnssData() {}

        /// Common constructor.
        gnssData( const HEADER_CLASS& h,
                  const BODY_CLASS& b )
        {
            header = h;
            body = b;
        }


        /// Copy constructor.
        template<class H, class B>
        gnssData(const gnssData<H,B>& g)
        {
            header = g.header;
            body = g.body;
        }


        /// Destructor.
        virtual ~gnssData() {};

    };  // End of 'gnssData'


      /// GNSS data structure with source and epoch as header
      /// (common indexes) and satTypeValueMap as body.
    struct  gnssRinex : gnssData<gnssRinexHeader, satTypeValueMap>
    {

         /// Return the number of satellites available in the body,
         /// which is a satTypeValueMap.
        size_t numSats() const
        { return body.numSats(); };

         /// Return the number of satellites available in the body,
         /// which is a satTypeValueMap.
        size_t numSats(SatelliteSystem::Systems sys) const
        { return body.numSats(sys); };

         /// Return a TypeIDSet with all the data types present in
         /// this object.
        TypeIDSet getTypeID() const
        { return (*this).body.getTypeID(); }


         /// Return a SatIDSet with all the satellites present in this object.
        SatIDSet getSatID() const
        { return (*this).body.getSatID(); }

         /// Return a SatIDSet with all the satellites present in this object.
        SatIDSet getSatID(SatelliteSystem::Systems sys) const
        { 
            SatIDSet satSet;
            for(auto stv: (*this).body)
            {
                if(stv.first.system == sys)
                {
                    satSet.insert(stv.first);
                }
            }
            return satSet; 
        }

         /// Return a Vector with all the satellites present in this object.
        Vector<SatID> getVectorOfSatID() const
        { return (*this).body.getVectorOfSatID(); }


         /** Return the total number of data elements in the body.
          * This method DOES NOT suppose that all the satellites have
          * the same number of type values.
          */
        size_t numElements() const
        { return body.numElements(); };


         /// Return a gnssRinex with only this satellite.
         /// @param satellite Satellite to be extracted.
        gnssRinex extractSatID(const SatID& satellite) const;


         /// Return a gnssRinex with only one satellite, identified
         /// by the given parameters.
         /// @param p Satellite PRN number.
         /// @param p System the satellite belongs to.
        gnssRinex extractSatID( const int& p,
                                       const SatelliteSystem::Systems& s ) const;


         /// Return a gnssRinex with only these satellites.
         /// @param satSet Set (SatIDSet) containing the satellites
         ///               to be extracted.
        gnssRinex extractSatID(const SatIDSet& satSet) const;


         /// Modifies this object, keeping only this satellite.
         /// @param satellite Satellite to be kept.
        gnssRinex& keepOnlySatID(const SatID& satellite);


         /// Modifies this object, keeping only this satellite.
         /// @param p Satellite PRN number.
         /// @param p System the satellite belongs to.
        gnssRinex& keepOnlySatID( const int& p,
                                         const SatelliteSystem::Systems& s );


         /// Modifies this object, keeping only these satellites.
         /// @param satSet Set (SatIDSet) containing the satellites to be kept.
        gnssRinex& keepOnlySatID(const SatIDSet& satSet);


         /// Return a gnssRinex with only this type of data.
         /// @param type Type of value to be extracted.
        gnssRinex extractTypeID(const TypeID& type) const;


         /// Return a gnssRinex with only these types of data.
         /// @param typeSet Set (TypeIDSet) containing the types of data
         ///                to be extracted.
        gnssRinex extractTypeID(const TypeIDSet& typeSet) const;


         /// Modifies this object, keeping only this type of data.
         /// @param type Type of value to be kept.
        gnssRinex& keepOnlyTypeID(const TypeID& type);


         /// Modifies this object, keeping only these types of data.
         /// @param typeSet Set (TypeIDSet) containing the types of data
         ///                to be kept.
        gnssRinex& keepOnlyTypeID(const TypeIDSet& typeSet);


         /// Modifies this object, removing this satellite.
         /// @param satellite Satellite to be removed.
        gnssRinex& removeSatID(const SatID& satellite)
        { (*this).body.erase(satellite); return (*this); }


         /// Modifies this object, removing these satellites.
         /// @param satSet Set (SatIDSet) containing the satellites
         ///               to be removed.
        gnssRinex& removeSatID(const SatIDSet& satSet);


         /// Modifies this object, removing this type.
         /// @param type Type to be removed.
        gnssRinex& removeTypeID(const TypeID& type)
        { (*this).body.removeTypeID(type); return (*this); }


         /// Modifies this object, removing these types of data.
         /// @param typeSet Set(TypeIDSet) containing the types
         ///               to be removed.
        gnssRinex& removeTypeID(const TypeIDSet& typeSet);


         /// Return a GPSTk::Vector containing the data values with this type.
         /// @param type Type of value to be returned.
        Vector<double> getVectorOfTypeID(const TypeID& type) const
        { return ( (*this).body.getVectorOfTypeID(type) ); }

        Matrix<double> getMatrixOfTypes(const TypeIDSet& typeSet) const
        { return ( (*this).body.getMatrixOfTypes(typeSet) ); }

        Matrix<double> getMatrixOfTypes(const TypeIDSet& typeSet, 
                                        SatelliteSystem::Systems sys) const
        { return ( (*this).body.getMatrixOfTypes(typeSet, sys) ); }

         /// Return a GPSTk::Vector containing the data values with this type.
         /// @param type Type of value to be returned.
        Vector<double> getVectorOfTypeID(const TypeID& type, SatelliteSystem::Systems sys) const
        { return ( (*this).body.getVectorOfTypeID(type, sys) ); }


         /** Modifies this object, adding one vector of data with this type,
          *  one value per satellite.
          *
          * If type already exists, data is overwritten. If the number of
          * values does not match with the number of satellites, a
          * NumberOfSatsMismatch exception is thrown.
          *
          * Given that dataVector does not store information about the
          * satellites the values correspond to, the user is held responsible
          * for having the data values stored in dataVector in the proper order
          * regarding the SatIDs in this object.
          *
          * @param type          Type of data to be added.
          * @param dataVector    GPSTk Vector containing the data to be added.
          */
        gnssRinex& insertTypeIDVector( const TypeID& type,
                                              const Vector<double> dataVector )
            noexcept(false)
        { (*this).body.insertTypeIDVector(type, dataVector); return (*this); }


         /** Modifies this object, adding a matrix of data, one vector
          *  per satellite.
          *
          * If types already exists, data is overwritten. If the number of
          * rows in matrix does not match with the number of satellites, a
          * NumberOfSatsMismatch exception is thrown. If the number of columns
          * in matrix does not match with the number of types in typeSet, a
          * NumberOfTypesMismatch exception is thrown.
          *
          * Given that dataMatrix does not store information about the
          * satellites and types the values correspond to, the user is held
          * responsible for having those data values stored in dataMatrix in
          * the proper order regarding the SatIDs in this object and the
          * provided typeSet.
          *
          * @param typeSet    Set (TypeIDSet) containing the types of data
          *                   to be added.
          * @param dataMatrix GPSTk Matrix containing the data to be added.
          */
        gnssRinex& insertMatrix( const TypeIDSet& typeSet,
                                        const Matrix<double> dataMatrix )
            noexcept(false)
        { (*this).body.insertMatrix(typeSet, dataMatrix); return (*this); }


         /** Return the data value (double) corresponding to provided SatID
          *  and TypeID.
          *
          * @param satellite     Satellite to be looked for.
          * @param type          Type to be looked for.
          */
        double getValue( const SatID& satellite,
                         const TypeID& type ) const
            noexcept(false)
        { return (*this).body.getValue( satellite, type ); }


         /** Return a reference to the typeValueMap with corresponding
          *  satellite.
          *
          * This operator allows direct access to data values when chained
          * with the typeValueMap::operator(), like this:
          *
          *    gRin(sat21)(TypeID::C1).
          *
          * Example:
          *
          * @code
          *   // Create the input file stream
          *   RinexObsStream rin("bahr1620.04o");
          *
          *   // Declare a gnssRinex object
          *   gnssRinex gRin;
          *
          *   // Create a satellite object
          *   SatID sat21(21,SatID::systemGPS);
          *
          *   // Feed the gRin data structure
          *   while(rin >> gRin)
          *   {
          *      try
          *      {
          *          if (gRin(sat21)(TypeID::C1) == 0.0)
          *          {
          *             gRin(sat21)(TypeID::C1) = 123.456;
          *          }
          *
          *          cout << "C1 value for satellite G21: "
          *               << gRin(sat21)(TypeID::C1) << endl;
          *      }
          *      catch (SatIDNotFound& e)
          *      {
          *          cout << endl << "Satellite G21 not found." << endl;
          *      };
          *   }
          * @endcode
          *
          * @param satellite Satellite to be looked for.
          *
          * @warning Please be aware that this operator doesn't mantain the
          * 'constness' of the data structure, allowing direct access to all
          * data (including editing). If this is not what you want, use method
          * 'getValue()' instead.
          */
        typeValueMap& operator()(const SatID& satellite)
            noexcept(false)
        { return (*this).body(satellite); }


         /// Return a gnssRinex with only these types of data.
         /// @param satSys Satellite System value to be kept.
        gnssRinex& keepOnlySatSystem(const SatelliteSystem::Systems satSys);


         /// Convenience output method
        virtual std::ostream& dump( std::ostream& s,
                                    int mode = 0 ) const;

         /// Destructor.
        virtual ~gnssRinex() {};


    };  // End of 'gnssRinex'


    //// Some other handy data structures


      /// GNSS data structure consisting in a map with SourceID as keys, and
      /// satTypeValueMap as elements.
    struct sourceDataMap : std::map<SourceID, satTypeValueMap>
    {

         /// Default constructor
        sourceDataMap() {};


         /** Return the data value (double) corresponding to provided SourceID,
          *  SatID and TypeID.
          *
          * @param source        Source to be looked for.
          * @param satellite     Satellite to be looked for.
          * @param type          Type to be looked for.
          */
        double getValue( const SourceID& source,
                         const SatID& satellite,
                         const TypeID& type ) const
            noexcept(false);


         /** Get a set with all the SourceID's in this data structure.
          *
          * @warning If current 'sourceDataMap' is big, this could be a very
          * costly operation.
          */
        SourceIDSet getSourceIDSet( void ) const;


         /** Get a set with all the SatID's in this data structure.
          *
          * @warning If current 'sourceDataMap' is big, this could be a very
          * costly operation.
          */
        SatIDSet getSatIDSet( void ) const;


         /// Destructor.
        virtual ~sourceDataMap() {};

    };    // End of 'sourceDataMap'



      /// GNSS data structure consisting in a map with CommonTime as keys, and
      /// sourceDataMap as elements.
    struct  sourceRinex : gnssData<sourceRinexHeader, sourceDataMap>
    {

         /// Default constructor
        sourceRinex() {};


         /** Adds 'gnssRinex' object data to this structure.
          *
          * @param gds     gnssRinex object containing data to be added.
          */
        sourceRinex& addGnssRinex( const gnssRinex& gds );


         /** Return a 'gnssRinex' object corresponding to given SourceID.
          *
          * @param source     SourceID object.
          *
          * @warning Returned data will correspond to first matching SourceID,
          * if it exists.
          */
        gnssRinex getGnssRinex( const SourceID& source ) const;


         /** Return a 'gnssRinex' object corresponding to given SatID.
          *
          * @param sate       SatID object.
          *
          * @warning
          * The first gnssRinex which contains the given SatID is returned.
          */
        gnssRinex getGnssRinex( const SatID& sat ) const;


         /** Adds 'sourceRinex' object data to this structure.
          *
          * @param gds     sourceRinex object containing data to be added.
          */
        sourceRinex& addSourceRinex( const sourceRinex& gds );



         /** Return the data value (double) corresponding to the first epoch
          *  in the data structure, given SourceID, SatID and TypeID.
          *
          * @param source        Source to be looked for.
          * @param satellite     Satellite to be looked for.
          * @param type          Type to be looked for.
          *
          * @warning If within first epoch (epoch +/- tolerance) more than one
          * match exists, then only the first one is returned.
          */
        double getValue( const SourceID& source,
                         const SatID& satellite,
                         const TypeID& type ) const
            noexcept(false);



         /** Inserts a data value (double) in the first epoch of the data
          *  structure with the given SourceID, SatID and TypeID.
          *
          * @param source        Source to be looked for.
          * @param satellite     Satellite to be looked for.
          * @param type          Type of the new data.
          * @param value         Value to be inserted.
          */
        sourceRinex& insertValue( const SourceID& source,
                                  const SatID& satellite,
                                  const TypeID& type,
                                  double value )
            noexcept(false);


         /** Get a set with all the SourceID's in this data structure.
          *
          * @warning If current 'sourceRinex' is big, this could be a very
          * costly operation.
          */
        SourceIDSet getSourceIDSet( void ) const;


         /** Get a set with all the SatID's in this data structure.
          *
          * @warning If current 'sourceRinex' is big, this could be a very
          * costly operation.
          */
        SatIDSet getSatIDSet( void ) const;


         /// Convenience output method
        virtual std::ostream& dump( std::ostream& s,
                                    int mode = 0 ) const;


         /// Return a sourceRinex with only this source.
         /// @param source Source to be extracted.
        sourceRinex extractSourceID(const SourceID& source);


         /// Return a sourceRinex with only these sources.
         /// @param sourceSet Set(SourceIDSet) containing the sources
         ///                  to be extracted.
        sourceRinex extractSourceID(const SourceIDSet& sourceSet);


         /// Modifies this object, keeping only this source.
         /// @param source Source to be extracted.
        sourceRinex& keepOnlySourceID(const SourceID& source);


         /// Modifies this object, keeping only these sources.
         /// @param sourceSet Set(SourceIDSet) containing the sources
         ///                  to be extracted.
        sourceRinex& keepOnlySourceID(const SourceIDSet& sourceSet);


         /// Modifies this object, removing this source.
         /// @param source Source to be removed.
        sourceRinex& removeSourceID(const SourceID& source);


         /// Modifies this object, keeping only these sources.
         /// @param sourceSet Set(SourceIDSet) containing the sources
         ///                  to be removed.
        sourceRinex& removeSourceID(const SourceIDSet& sourceSet);


         /// Return a sourceRinex with only this satellite.
         /// @param sat Satellite to be extracted.
        sourceRinex extractSatID(const SatID& sat);


         /// Return a sourceRinex with only these satellites.
         /// @param satSet Set(SatIDSet) containing the satellite
         ///               to be extracted.
        sourceRinex extractSatID(const SatIDSet& satSet);


         /// Modifies this object, keeping only this satellite.
         /// @param sat Satellite to be extracted.
        sourceRinex& keepOnlySatID(const SatID& sat);


         /// Modifies this object, keeping only these satellites.
         /// @param satSet Set(SatIDSet) containing the satellite
         ///                  to be extracted.
        sourceRinex& keepOnlySatID(const SatIDSet& satSet);


         /// Modifies this object, removing this satellite.
         /// @param sat Satellite to be removed.
        sourceRinex& removeSatID(const SatID& sat);


         /// Modifies this object, remove these satellites.
         /// @param satSet Set(SatIDSet) containing the satellites
         ///               to be removed.
        sourceRinex& removeSatID(const SatIDSet& satSet);


         /// Modifies this object, remove this satellite of this source.
         /// @param source  Source to be removed.
         /// @param sat     Satellite to be removed.
        sourceRinex& removeSatID(const SourceID& source,
                                 const SatID& sat);



         /// Return a sourceRinex with only this type.
         /// @param type Type to be extracted.
        sourceRinex extractTypeID(const TypeID& type);


         /// Return a sourceRinex with only these satellites.
         /// @param typeSet Set(TypeIDSet) containing the types
         ///               to be extracted.
        sourceRinex extractTypeID(const TypeIDSet& typeSet);


         /// Modifies this object, keeping only this type.
         /// @param type Type to be extracted.
        sourceRinex& keepOnlyTypeID(const TypeID& type);


         /// Modifies this object, keeping only these types.
         /// @param typeSet Set(TypeIDSet) containing the type
         ///                  to be extracted.
        sourceRinex& keepOnlyTypeID(const TypeIDSet& typeSet);


         /// Modifies this object, removing this type.
         /// @param type Type to be removed.
        sourceRinex& removeTypeID(const TypeID& type);


         /// Modifies this object, removing these types of data.
         /// @param typeSet     Set(TypeIDSet) containing the types
         ///                    to be removed.
        sourceRinex& removeTypeID(const TypeIDSet& typeSet);


         /// Modifies this object, remove this satellite of this source
         /// and this satellite.
         /// @param source  Source to be removed.
         /// @param sat     Satellite to be removed.
         /// @param type    Type to be removed.
        sourceRinex& removeTypeID(const SourceID& source,
                                  const SatID& sat,
                                  const TypeID& type);

         /// Return a gnssRinex with only these types of data.
         /// @param satSys Satellite System value to be kept.
        sourceRinex& keepOnlySatSystem(const SatelliteSystem::Systems satSys);

         /// Destructor.
        virtual ~sourceRinex() {};

    };    // End of 'sourceRinex'






      /// Object defining the structure of a GNSS equation. The header is the
      /// prefit and the body is a TypeIDSet containing the unknowns.
    struct  gnssEquationDefinition : gnssData<TypeID, TypeIDSet>
    {

         /// Default constructor.
        gnssEquationDefinition() {};


         /// Common constructor.
        gnssEquationDefinition( const TypeID& h,
                                const TypeIDSet& b )
        {
            header = h;
            body   = b;
        }


         /// Destructor.
        virtual ~gnssEquationDefinition() {};


    };  // End of 'gnssEquationDefinition'



      /// Object defining the structure of a GNSS linear combination. The
      /// header is the result type and the body is a typeValueMap containing
      /// the GNSS data types to be combined plus corresponding coefficients.
    struct  gnssLinearCombination : gnssData<TypeID, typeValueMap>
    {

         /// Default constructor.
        gnssLinearCombination() {};


         /// Common constructor.
        gnssLinearCombination( const TypeID& h,
                               const typeValueMap& b )
        {
            header = h;
            body   = b;
        }


         /// Destructor.
        virtual ~gnssLinearCombination() {};


    };  // End of 'gnssLinearCombination'


      /// List containing gnssLinearCombination objects.
    typedef std::list<gnssLinearCombination> LinearCombList;


      /// Convenience function to convert from SatID system to SourceID type.
      /// @param sid Satellite ID.
    SourceID::SourceType SatIDsystem2SourceIDtype(const SatID& sid);


/*
      /// Convenience function to fill a satTypeValueMap with data
      /// from RinexObsData.
      /// @param roh RinexObsHeader holding the data
      /// @param rod RinexObsData holding the data.
    satTypeValueMap satTypeValueMapFromRinexObsData(const RinexObsHeader& roh,
                                                    const RinexObsData& rod);
*/


      /// Convenience function to fill a satTypeValueMap with data
      /// from Rinex3ObsData.
      /// @param roh Rinex3ObsHeader holding the data
      /// @param rod Rinex3ObsData holding the data.
    satTypeValueMap satTypeValueMapFromRinex3ObsData(const Rinex3ObsHeader& roh,
                                                     const Rinex3ObsData& rod);






      /// stream output for data 
    std::ostream& operator<<( std::ostream& s, const sourceRinex& gdsMap);
    std::ostream& operator<<( std::ostream& s, const typeValueMap& tvMap);
    std::ostream& operator<<( std::ostream& s, const satValueMap& svMap );
    std::ostream& operator<<( std::ostream& s, const satTypeValueMap& stvMap);
    std::ostream& operator<<( std::ostream& s, const gnssRinex& f );

      /** Stream input for gnssRinex.
       *
       * This handy operator allows to fed a gnssRinex data structure
       * directly from an input stream such a RinexObsStream object.
       *
       * For example:
       *
       * @code
       *
       *   // Create the input file stream
       *   RinexObsStream rin("bahr1620.04o");
       *
       *   // Declare a gnssRinex object
       *   gnssRinex gRin;
       *
       *   // Feed the gRin data structure
       *   while( rin >> gRin )
       *   {
       *       // Lots of stuff in here...
       *   }
       * @endcode
       */
    std::istream& operator>>( std::istream& i, gnssRinex& f );



      /** Stream output for gnssRinex.
       *
       * This handy operator allows to output a gnssRinex data structure
       * directly to an output stream such a RinexObsStream object.
       *
       * The RinexObsHeader object of output stream should be initialized
       * correctly before any output operation.
       *
       * For example:
       *
       * @code
       *   // Create the input file stream
       *   RinexObsStream rin("bahr1620.04o");
       *
       *   //Create the output file stream
       *   RinexObsStream rout("bahr1620.04o.new", ios::out|ios::trunc);
       *
       *   // Read the RINEX header
       *   RinexObsHeader head; //RINEX header object
       *   rin >> head;
       *   rout.header = rin.header;
       *
       *   rout << rout.header;
       *
       *   gnssRinex gRin;
       *   while( rin >> gRin )
       *   {
       *       rout << gRin
       *   }
       * @endcode
       */
    Rinex3ObsStream& operator<<(Rinex3ObsStream& s, gnssRinex& f );

    typedef std::map<CommonTime, gnssRinex> epochGnssRinexMap;
    typedef std::map<CommonTime, sourceRinex> epochSourceRinexMap;
    typedef std::map<CommonTime, sourceDataMap> epochSourceDataMap;


      //@}

}  // End of namespace gpstk

#endif // GPSTK_DATASTRUCTURES_HPP
